/**
  * <CODE>NumberToWordsConverter</CODE> is a small utility class which converts a
  * number into a string of words which represent that number.  This can be
  * used in a number of situations, such as a financial application where
  * cheques need to be printed with numeric and textual representations of the
  * cheque amount.
  *
  * The coverage of the conversion process is good even though we can only
  * deal with a maximum multiplier of millions.  Numbers higher than this are
  * recursivly converted in to an amount of millions so that all values can
  * be dealt with :
  *
  * <PRE>
  *  toWords(1)          = one
  *  toWords(12)         = twelve
  *  toWords(123)        = one hundred and twenty three
  *  toWords(1234)       = one thousand, two hundred and thirty four
  *  toWords(12345)      = twelve thousand, three hundred and fourty five
  *  toWords(123456)     = one hundred and twenty three thousand, four hundred and fifty six
  *  toWords(1234567)    = one million, two hundred and thirty four thousand, five hundred and sixty seven
  *  toWords(12345678)   = twelve million, three hundred and fourty five thousand, six hundred and seventy eight
  *  toWords(123456789)  = one hundred and twenty three million, four hundred and fifty six thousand, seven hundred and eighty nine
  *  toWords(1234567890) = one thousand, two hundred and thirty four million, five hundred and sixty seven thousand, eight hundred and ninety
  * </PRE>
  */
package com.dcivision.framework;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class NumberToWordsConverter {

  public static final String REVISION = "$Revision: 1.3.32.1 $";

  private static final Log log = LogFactory.getLog(NumberToWordsConverter.class);
  
  // --------------------------------
  // Define private member variables.
  // --------------------------------
  private static String[] unit        = new String[20];
  private static String[] tens        = new String[10];
  private static String[] multiplier  = new String[10];

  // -------------------------------
  // Define public member variables.
  // -------------------------------
  public static final int BASE        = 10;

  public static final int MAX_FACTOR  = 6;
  public static final int MIN_FACTOR  = 2;
  public static final int MAX_UNIT    = 9;
  public static final int MIN_UNIT    = 2;

  // Constructor
  public NumberToWordsConverter() {
    // Call super constructor.
    super();
  }

  public static void main(String[] arguments) {
    try {
      // Convert the first argument to words.
      System.out.println(NumberToWordsConverter.toCurrencyWords(Double.parseDouble(arguments[0])));
    } catch(Exception e) {
      // Dump the stack trace.
      log.error(e, e);
    }
  }

  // static variables
  static {
    // Initialise conversion strings.
    unit[0]  = "zero";
    unit[1]  = "one";
    unit[2]  = "two";
    unit[3]  = "three";
    unit[4]  = "four";
    unit[5]  = "five";
    unit[6]  = "six";
    unit[7]  = "seven";
    unit[8]  = "eight";
    unit[9]  = "nine";
    unit[10] = "ten";
    unit[11] = "eleven";
    unit[12] = "twelve";
    unit[13] = "thirteen";
    unit[14] = "fourteen";
    unit[15] = "fifteen";
    unit[16] = "sixteen";
    unit[17] = "seventeen";
    unit[18] = "eighteen";
    unit[19] = "nineteen";

    // Initialise conversion strings.
    tens[0] = "zero";
    tens[1] = "ten";
    tens[2] = "twenty";
    tens[3] = "thirty";
    tens[4] = "fourty";
    tens[5] = "fifty";
    tens[6] = "sixty";
    tens[7] = "seventy";
    tens[8] = "eighty";
    tens[9] = "ninety";

    // Initialise conversion strings.
    multiplier[0] = null;
    multiplier[1] = null;
    multiplier[2] = "hundred";
    multiplier[3] = "thousand";
    multiplier[4] = null;
    multiplier[5] = null;
    multiplier[6] = "million";
    multiplier[7] = "billion";
    multiplier[8] = "trillion";
    multiplier[9] = "squillion";
  }

  public static String toWords(double number) {
    String result = convert((int)number);
    return(result);
  }

  public static String toCurrencyWords(double number) {
    String result = toCurrencyWords((int)number);
    if (number == 0.0D) {
      return("");
    }

    int decNum = (int)((number * 100.00D) - (((int)number) * 100.00D));
    if (decNum > 0) {
      result += " " + toWords(decNum) + " cents";
    }

    return(result);
  }

  public static String toWords(int number) {
    return convert(number);
  }

  public static String toCurrencyWords(int number) {
    String result = convert(number);
    if (number == 0 || number == 1) {
      result += " dollar";
    } else {
      result += " dollars";
    }
    return(result);
  }

  // convert
  private static String convert(int number) {
    // Define local variables.
    int head         = 0;
    int tail         = 0;
    int factor       = 0;

    String connector = null;

    // Check if the number is a minus number.
    if (number < 0) {
      return "minus " + convert( -number );
    } else {
      // Check if the number is outside the bounds of our highest multiplier.
      if (number > pow( BASE, MAX_FACTOR )) {
        // Get the head and tail of the number based on highest multiplier.
        head = div(number, pow(BASE, MAX_FACTOR));
        tail = mod(number, pow(BASE, MAX_FACTOR));

        // Check if there is a tail amount.
        if ( tail == 0 ) {
          // Convert the head recursively.
          return convert( head ) + " " + multiplier[MAX_FACTOR];
        } else {
          // Make sure we get the right connecting word.
          if (tail < pow(BASE, MIN_FACTOR)) {
            connector = " and ";
          } else {
            connector = ", ";
          }

          // Convert the head and tail recursively.
          return convert( head ) + " " + multiplier[MAX_FACTOR] + connector + convert( tail );
        }
      } else {
        // Loop through all the multipliers we can deal with.
        for (factor = MAX_FACTOR; factor >= MIN_FACTOR; factor--) {
          // If the number of bigger than the current multiplier, we have
          // got work to do otherwise carry on.
          if (number >= pow(BASE, factor)) {
            // Get the head and tail of the number based on current multiplier.
            head = div( number, pow( BASE, factor ) );
            tail = mod( number, pow( BASE, factor ) );

            // Check if the current multiplier is printable.
            if (multiplier[factor] != null) {
              // Check if there is a tail amount.
              if (tail == 0) {
                // Convert the head recursively.
                return convert( head ) + " " + multiplier[factor];
              } else {
                // Make sure we get the right connecting word.
                if(tail < pow(BASE, MIN_FACTOR)) {
                  connector = " and ";
                } else {
                  connector = ", ";
                }

                // Convert the head and tail recursively.
                return convert(head) + " " + multiplier[factor] + connector + convert(tail);
              }
            }
          }
        }

        // Loop through all the unit multipliers we can deal with.
        for (factor = MAX_UNIT; factor >= MIN_UNIT; factor--) {
          // If there is a unit multiplier present, we have work to do otherwise
          // carry on.
          if (factor == div(number, BASE)) {
            // Get the head and tail of the number based on current multiplier.
            head = div(number, BASE);
            tail = mod(number, BASE);

            // Check if there is a tail amount.
            if (tail == 0) {
              // Convert the head.
              return tens[head];
            } else {
              // Convert the head and tail.
              return tens[head] + " " + unit[tail];
            }
          }
        }

        // Convert the single unit based number.
        return unit[number];
      }
    }
  }

  private static int pow( int a, int b ) {
    return ( int )( Math.pow( a, b ) );
  }

  private static int mul( int a, int b ) {
    return ( int )( b * a );
  }

  private static int div( int a, int b ) {
    return ( int )( a / b );
  }

  private static int mod( int a, int b ) {
    return ( int )( a % b );
  }

}
